home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / linuxcon.000 / linuxcon / linuxconf-1.6 / translate / msgscan.c < prev    next >
C/C++ Source or Header  |  1996-04-14  |  8KB  |  381 lines

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <stdarg.h>
  5. #include <ctype.h>
  6. #include "../misc/misc.h"
  7. #include "internal.h"
  8.  
  9. // This table indicates the correspondance for the first
  10. // string and second string of the MSG_B macro.
  11. static char *tblang;
  12.  
  13. #ifdef UNIX
  14.     #include <limits.h>
  15.     #define MAXIMUM_PATH    PATH_MAX
  16. #else
  17.     #define MAXIMUM_PATH    128
  18. #endif
  19.  
  20. struct READINFO{
  21.     int noline;
  22.     char buf[1000];
  23.     FILE *fin;
  24. };
  25.  
  26. /*
  27.     Print an error message in a popup
  28.     Stubs to avoid linking the world
  29. */
  30. void xconf_error (const char *msg, ...)
  31. {
  32.     va_list list;
  33.     va_start (list,msg);
  34.     vfprintf (stderr,msg,list);
  35.     va_end (list);
  36. }
  37.  
  38. struct {
  39.     const char *fname;
  40.     int noline;
  41.     int nberr;
  42.     int showname;
  43.     int showline;    // First error for a context
  44. }err;
  45. /*
  46.     Record basic info about errors
  47. */
  48. static void msgscan_errset (const char *fname, int noline)
  49. {
  50.     if (err.fname == NULL || strcmp(err.fname,fname)!=0){
  51.         err.showname = 1;
  52.     }
  53.     err.fname = fname;
  54.     err.noline = noline;
  55.     err.showline = 1;
  56. }
  57.  
  58. /*
  59.     Display an error message with context
  60. */
  61. static void msgscan_err (const char *ctl, ...)
  62. {
  63.     va_list list;
  64.     va_start (list,ctl);
  65.     if (err.showname){
  66.         fprintf (stderr,"ERR: In file %s\n",err.fname);
  67.         err.showname = 0;
  68.     }
  69.     if (err.showline){
  70.         fprintf (stderr,"\t\tstarting on line %d\n",err.noline);
  71.         err.showline = 0;
  72.     }
  73.     fprintf (stderr,"\t\t\t");
  74.     vfprintf (stderr,ctl,list);
  75.     va_end (list);
  76. }
  77.  
  78. static int msgscan_read (READINFO &inf, int signal_err)
  79. {
  80.     int ret = fgets_cont (inf.buf,sizeof(inf.buf)-1,inf.fin);
  81.     inf.noline++;
  82.     if (ret == -1 && signal_err){
  83.         msgscan_err ("EOF while parsing\n");
  84.     }
  85.     return ret;
  86. }
  87.  
  88.  
  89.  
  90. static char *scan_copyid (const char * pt, char id[])
  91. {
  92.     pt = str_skip (pt);
  93.     char *ptid = id;
  94.     while (isalpha(*pt) || isdigit(*pt) || *pt == '_'){
  95.         *ptid++ = *pt++;
  96.     }
  97.     *ptid = '\0';
  98.     return (char*)pt;
  99. }
  100.  
  101.  
  102. static int scan_parseid (char * &pt, char id[])
  103. {
  104.     int ret = -1;
  105.     pt = scan_copyid (pt,id);
  106.     if (id[0] != '\0'){
  107.         if (isdigit(id[0])){
  108.             msgscan_err ("Invalid id %s\n",id);
  109.         }else{
  110.             ret = 0;
  111.         }
  112.     }
  113.     return ret;
  114. }
  115.  
  116.  
  117. /*
  118.     Extract a single string from a C source.
  119. */
  120. static int scan_parse1str (char * &pt, SSTRING &s)
  121. {
  122.     int ret = -1;
  123.     pt = str_skip (pt);
  124.     if (*pt != '"'){
  125.         msgscan_err ("Expected an openning \"\n");
  126.     }else{
  127.         pt++;
  128.         char *start = pt;
  129.         while (1){
  130.             if (*pt == '\0'){
  131.                 msgscan_err ("Expected an ending \"\n");
  132.                 break;
  133.             }else if (*pt == '"'){
  134.                 ret = 0;
  135.                 *pt++ = '\0';
  136.                 s.append (start);
  137.                 break;
  138.             }else if (*pt == '\\'){
  139.                 pt++;
  140.                 if (*pt != '\0') pt++;
  141.             }else{
  142.                 pt++;
  143.             }
  144.             
  145.         }
  146.     }
  147.     return ret;
  148. }
  149. /*
  150.     Extract a string (or sequence) from a C source. A string may be made
  151.     of several strings concatenated and potentially spreaded on
  152.     several lines.
  153. */
  154. static int scan_parsestr (
  155.     char * &pt,
  156.     SSTRING &s,
  157.     READINFO &inf)
  158. {
  159.     int ret = -1;
  160.     s.setfrom ("");
  161.     while (1){
  162.         pt = str_skip (pt);
  163.         if (*pt == '\0'){
  164.             if (msgscan_read(inf,1)==-1){
  165.                 break;
  166.             }
  167.             pt = inf.buf;
  168.         }else if (*pt != '"'){
  169.             if (ret == -1){
  170.                 msgscan_err ("Expected an openning \"\n");
  171.             }
  172.             break;
  173.         }else if (scan_parse1str(pt,s)==-1){
  174.             ret = -1;
  175.             break;
  176.         }else{
  177.             // One string was seen, looking for others
  178.             ret = 0;
  179.         }
  180.     }
  181.     return ret;
  182. }
  183.  
  184. /*
  185.     Parse a MSG_x() macro to extract the ID and the strings
  186.     Return the pointer after the closing parenthese.
  187. */
  188. static char *scan_parse(
  189.     char *pt,
  190.     TR_STRINGS &tr,
  191.     READINFO &inf,
  192.     int nbsreq)    // Number of strings expected
  193. {
  194.     /* #Specification: translation / message / encoding in source
  195.         Messages are created in C (C++) source files and
  196.         extracted with a utility (msgscan). A message is
  197.         defined like this
  198.  
  199.         #
  200.         // This is a message without translation
  201.         MSG_U(msgid,"message's text")
  202.         // This is a message with a proposed translation
  203.         MSG_B(msgid,"message's text","Texte du message")
  204.         #
  205.  
  206.         msgid is a unique identifier generally composed of
  207.         letters and number. It must respected lexical
  208.         convention for macros (as it will be #defined)
  209.     */
  210.     pt = str_skip (pt);
  211.     int last_nberr = err.nberr;
  212.     if (pt[0] == '('){
  213.         char id[100];
  214.         if (scan_parseid(++pt,id)!=-1){
  215.             SSTRING tbs[2];
  216.             int nbs = 0;
  217.             while (1){
  218.                 pt = str_skip (pt);
  219.                 if (pt[0] == '\0'){
  220.                     // We must refill the buffer
  221.                     if (msgscan_read (inf,1)==-1){
  222.                         break;
  223.                     }
  224.                     pt = inf.buf;
  225.                 }else if (*pt == ')'){
  226.                     pt++;
  227.                     break;
  228.                 }else if (*pt != ','){
  229.                     msgscan_err ("expected comma after id\n");
  230.                     break;
  231.                 }else if (nbs == nbsreq){
  232.                     msgscan_err ("Too many strings\n");
  233.                     break;
  234.                 }else if (scan_parsestr(++pt,tbs[nbs],inf)==0){
  235.                     nbs++;
  236.                 }else{
  237.                     break;
  238.                 }
  239.             }
  240.             if (err.nberr == last_nberr){
  241.                 if (nbs != nbsreq){
  242.                     msgscan_err ("Not enough strings supplied\n");
  243.                 }else{
  244.                     TR_STRING *t = tr.getitem(id);
  245.                     if (t == NULL){
  246.                         t = new TR_STRING (id);
  247.                         tr.add (t);
  248.                     }
  249.                     if (t->was_changed()){
  250.                         msgscan_err ("Duplicate translation id %s\n"
  251.                             ,id);
  252.                         const char *ori = t->getorigin();
  253.                         msgscan_err (" It was defined in %s\n",ori);
  254.                         msgscan_err (" Previous definition was \"%s\"\n"
  255.                             ,t->getmsg(tblang[0]));
  256.                         msgscan_err (" New definition is \"%s\"\n",tbs[0].get());
  257.                     }else{
  258.                         t->setorigin (err.fname);
  259.                         for (int i=0; i<nbs; i++){
  260.                             t->settranslation (tblang[i]
  261.                                 ,tbs[i].get());
  262.                         }
  263.                     }
  264.                 }
  265.             }
  266.         }
  267.     }else{
  268.         msgscan_err("Expected (\n");
  269.     }
  270.     if (err.nberr != last_nberr){
  271.         // We skip this line to avoid falling always on the same
  272.         // error.
  273.         while (*pt != '\0') pt++;
  274.     }
  275.     return pt;
  276. }
  277.  
  278.  
  279.  
  280. /*
  281.     Locate all messages in a source file and update the
  282.     dictionary.
  283.     Return -1 if any error
  284. */
  285. int scan_one (const char *fname, TR_STRINGS &tr)
  286. {
  287.     int ret = -1;
  288.     READINFO inf;
  289.     inf.noline = 0;
  290.     inf.fin = vfopen (fname,"r");
  291.     if (inf.fin == NULL){
  292.         fprintf (stderr,"Can't open file %s (%s)\n",fname
  293.             ,strerror(errno));
  294.     }else{
  295.         ret = 0;
  296.         while (msgscan_read (inf,0)!=-1){
  297.             char *pt = inf.buf;
  298.             // Maybe more than one message on the line
  299.             while (1){
  300.                 pt = strstr (pt,"MSG");
  301.                 if (pt != NULL){
  302.                     char verb[1000];
  303.                     pt = scan_copyid (pt,verb);
  304.                     if (strcmp(verb,"MSG_U")==0){
  305.                         msgscan_errset (fname,inf.noline);
  306.                         pt = scan_parse (pt,tr,inf,1);
  307.                     }else if (strcmp(verb,"MSG_B")==0){
  308.                         msgscan_errset (fname,inf.noline);
  309.                         pt = scan_parse (pt,tr,inf,2);
  310.                     }
  311.                 }else{
  312.                     break;
  313.                 }
  314.             }
  315.         }
  316.     }
  317.     return ret;
  318. }
  319.  
  320. #if 0
  321. int scan_vone (const char *fname, TR_STRINGS &tr)
  322. {
  323.     int ret = 0;
  324.     char *tb[1000];
  325.     int nb = vdir_getlistp (fname,tb,1000);
  326.     for (int i=0; i<nb; i++){
  327.         ret |= scan_one (tb[i],tr);
  328.     }
  329.     return ret;
  330. }
  331. #endif
  332. static void usage()
  333. {
  334.     fprintf (stderr,
  335.         "msgscan 1.0\n"
  336.         "msgscan sysname dictionary header lang_letters source source ...\n"
  337.         "\n"
  338.         "Update the dictionary file with source (C C++ source)\n"
  339.         "The file dictionary will be the ascii dictionary\n"
  340.         "the file \"header\" will be produced\n"
  341.         "and must be included in every source files using the dictionary\n"
  342.         "\n"
  343.         "lang_letters is a string of 2 letters use to tag the first and\n"
  344.         "second string extracted from an MSG_U/MSG_B macro\n"
  345.         "\n"
  346.         "Normal usage for the linuxconf project\n"
  347.         "\tmsgscan netconf ../messages/sources/netconf.dic netconf.m EF *.c\n"
  348.         );
  349. }
  350.  
  351. int main (int argc, char *argv[])
  352. {
  353.     int ret = -1;
  354.     if (argc < 6){
  355.         usage();
  356.     }else{
  357.         TR_STRINGS tr;
  358.         const char *sys = argv[1];
  359.         const char *pathdict = argv[2];
  360.         const char *pathincl = argv[3];
  361.         tblang = argv[4];
  362.         tr.read (pathdict);
  363.         tr.deltranslation (tblang[0]);
  364.         tr.deltranslation (tblang[1]);
  365.         ret = 0;
  366.         for (int i=5; i<argc; i++){
  367.             ret |= scan_one (argv[i],tr);
  368.         }
  369.         if (ret == 0){
  370.             char path[MAXIMUM_PATH];
  371.             sprintf (path,"%s.old",pathdict);
  372.             rename (pathdict,path);
  373.             tr.write (pathdict);
  374.             tr.writeh (sys,pathincl);
  375.             tr.showold (tblang[0]);
  376.         }
  377.     }
  378.     return ret;
  379. }
  380.  
  381.